跳到主要内容

聊天室:收藏

最后我们再来实现下收藏功能:

聊天的时候可以收藏某条消息,然后在收藏列表查看。

消息分为文字、图片、文件三类,收藏也是这三类。

我们先加一个收藏表:

model Favorite {
id Int @id @default(autoincrement())
chatHistoryId Int
uerId Int
createTime DateTime @default(now())
updateTime DateTime @updatedAt
}

每个收藏只要记录对应的 chatHistoryId 和 userId 即可。

这里我们同样没有用外键。

用 migrate dev 生成表:

npx prisma migrate dev --name favorite

sql 没啥问题:

然后创建一个模块:

nest g resource favorite

在 FavorateController 添加三个路由:

import { Controller, Get, Query } from "@nestjs/common";
import { FavoriteService } from "./favorite.service";
import { RequireLogin, UserInfo } from "src/custom.decorator";

@Controller("favorite")
@RequireLogin()
export class FavoriteController {
constructor(private readonly favoriteService: FavoriteService) {}

@Get("list")
async list(@UserInfo("userId") userId: number) {
return this.favoriteService.list(userId);
}

@Get("add")
async add(
@UserInfo("userId") userId: number,
@Query("chatHistoryId") chatHistoryId: number
) {
return this.favoriteService.add(userId, chatHistoryId);
}

@Get("del")
async del(@Query("id") id: number) {
return this.favoriteService.del(id);
}
}

list、add、del 这三个路由都需要登录,在 Controller 上加上 @RequireLogin 装饰器。

然后取 request.user 里的 userId 传入 handler。

分别在 service 实现这三个方法:

import { Inject, Injectable } from "@nestjs/common";
import { PrismaService } from "src/prisma/prisma.service";

@Injectable()
export class FavoriteService {
@Inject(PrismaService)
private prismaService: PrismaService;

async list(userId: number) {
const favorites = await this.prismaService.favorite.findMany({
where: {
uerId: userId,
},
});
const res = [];
for (let i = 0; i < favorites.length; i++) {
const chatHistory = await this.prismaService.chatHistory.findUnique(
{
where: {
id: favorites[i].chatHistoryId,
},
}
);
res.push({
...favorites[i],
chatHistory,
});
}
return res;
}

async add(userId: number, chatHistoryId: number) {
return this.prismaService.favorite.create({
data: {
uerId: userId,
chatHistoryId,
},
});
}

async del(id: number) {
return this.prismaService.favorite.deleteMany({
where: {
id,
},
});
}
}

list 方法把关联的 chatHistory 查出来。

测试下:

添加两条收藏:

查看下:

删除一条:

查看下:

这样,接口就都完成了。

我们再写下前端页面:

先在 interfaces 添加几个接口:

export async function queryFavoriteList() {
return axiosInstance.get(`/favorite/list`);
}

export async function favoriteAdd(chatHistoryId: number) {
return axiosInstance.get(`/favorite/add`, {
params: {
chatHistoryId,
},
});
}

export async function favoriteDel(id: number) {
return axiosInstance.get(`/favorite/del`, {
params: {
id,
},
});
}

写下页面 src/pages/Collection/index.tsx

import { Table, message } from "antd";
import { useEffect, useState } from "react";
import { ColumnsType } from "antd/es/table";
import { queryFavoriteList } from "../../interfaces";

interface Favorite {
id: number;
chatHistory: {
id: number;
content: string;
type: number;
createTime: Date;
};
}

export function Collection() {
const [favoriteList, setFavoriteList] = useState<Array<Favorite>>([]);

const columns: ColumnsType<Favorite> = [
{
title: "ID",
dataIndex: "id",
},
{
title: "内容",
render: (_, record) => (
<div>
{record.chatHistory.type === 0 ? (
record.chatHistory.content
) : record.chatHistory.type === 1 ? (
<img
src={record.chatHistory.content}
style={{ maxHeight: 200 }}
/>
) : (
<a href={record.chatHistory.content} download>
{record.chatHistory.content}
</a>
)}
</div>
),
},
{
title: "发表时间",
render: (_, record) => (
<div>
{new Date(record.chatHistory.createTime).toLocaleString()}
</div>
),
},
{
title: "操作",
render: (_, record) => (
<div>
<a href="" onClick={() => {}}>
删除
</a>
</div>
),
},
];

const query = async () => {
try {
const res = await queryFavoriteList();

if (res.status === 201 || res.status === 200) {
setFavoriteList(
res.data.map((item: Favorite) => {
return {
...item,
key: item.id,
};
})
);
}
} catch (e: any) {
message.error(e.response?.data?.message || "系统繁忙,请稍后再试");
}
};

useEffect(() => {
query();
}, []);

return (
<div id="friendship-container">
<div className="favorite-table">
<Table
columns={columns}
dataSource={favoriteList}
style={{ width: "1000px" }}
/>
</div>
</div>
);
}

就是请求列表接口,用 table 展示。

我们实现下收藏功能。

简化下交互,双击聊天记录触发收藏:

onDoubleClick={() => {
addToFavorite(item.id)
}}
async function addToFavorite(chatHistoryId: number) {
try {
const res = await favoriteAdd(chatHistoryId);

if (res.status === 201 || res.status === 200) {
message.success("收藏成功");
}
} catch (e: any) {
message.error(e.response?.data?.message || "系统繁忙,请稍后再试");
}
}

我们收藏几条消息:

提示收藏成功,之后在收藏页面就可以看到了:

然后再做下删除:

<Popconfirm
title="删除收藏"
description="确认删除吗?"
onConfirm={() => delFavorite(record.id)}
okText="Yes"
cancelText="No">
<a href="#">删除</a>
</Popconfirm>
async function delFavorite(id: number) {
try {
const res = await favoriteDel(id);

if (res.status === 201 || res.status === 200) {
message.success("删除成功");
query();
}
} catch (e: any) {
message.error(e.response?.data?.message || "系统繁忙,请稍后再试");
}
}

测试下:

没啥问题。

这样,收藏功能就完成了。

前端代码

后端代码

总结

这节我们实现了收藏功能。

首先创建了收藏表,关联 user 和 chatHistory。

然后创建了 list、add、del 三个接口。

之后在前端通过 table 展示 list 接口的数据,然后双击聊天记录的时候调用 add 添加收藏,点击删除的时候调用 del 删除收藏

这样就实现了收藏功能。